package cn.yh.register.order.service.impl;

import cn.yh.register.client.hosp.HospitalFeignClient;
import cn.yh.register.client.user.UsersFeignClient;
import cn.yh.register.common.constant.MqConst;
import cn.yh.register.common.exception.RegisterException;
import cn.yh.register.common.helper.HttpRequestHelper;
import cn.yh.register.common.result.ResultCodeEnum;
import cn.yh.register.common.service.RabbitService;
import cn.yh.register.enums.OrderStatusEnum;
import cn.yh.register.model.order.OrderInfo;
import cn.yh.register.model.user.Patient;
import cn.yh.register.order.mapper.OrderInfoMapper;
import cn.yh.register.order.service.OrderService;
import cn.yh.register.order.service.WeixinService;
import cn.yh.register.vo.hosp.ScheduleOrderVo;
import cn.yh.register.vo.msm.MsmVo;
import cn.yh.register.vo.order.*;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.junit.jupiter.api.Order;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collector;
import java.util.stream.Collectors;

@Slf4j
@Transactional(rollbackFor = Exception.class)
@Service
public class OrderServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderService {

    @Autowired
    UsersFeignClient usersFeignClient;
    @Autowired
    HospitalFeignClient hospitalFeignClient;
    @Autowired
    RabbitService rabbitService;
    @Autowired
    WeixinService weixinService;

    /**
     * 保存订单 & 对接医院系统下单接口
     * @param scheduleId 排班id
     * @param patientId  就诊人id
     * @return 订单编号
     */
    @Override
    public Long saveOrder(String scheduleId, Long patientId) {
        // 调用户服务获取就诊人信息
        Patient patient = usersFeignClient.getPatientOrder(patientId);
        if (patient == null) {
            throw new RegisterException(ResultCodeEnum.PARAM_ERROR);
        }
        // 调医院服务获取排班信息
        ScheduleOrderVo scheduleOrderVo = hospitalFeignClient.getScheduleOrderVo(scheduleId);
        // 判断当前时间是否可以下单

        if (new DateTime(scheduleOrderVo.getStartTime()).isAfterNow()
                || new DateTime(scheduleOrderVo.getEndTime()).isBeforeNow()) {
            throw new RegisterException(ResultCodeEnum.TIME_NO);
        }
        // 判断号源是否充足
        if (scheduleOrderVo.getAvailableNumber() <= 0) {
            throw new RegisterException(ResultCodeEnum.NUMBER_NO);
        }
        // 保存订单
        OrderInfo orderInfo = new OrderInfo();
        BeanUtils.copyProperties(scheduleOrderVo, orderInfo);
        // 订单号  系统毫秒值 +100以内的随机数
        orderInfo.setOutTradeNo(System.currentTimeMillis() + "" + new Random().nextInt(100));
        // 就诊人信息
        orderInfo.setUserId(patient.getUserId());
        orderInfo.setPatientId(patientId);
        orderInfo.setPatientName(patient.getName());
        orderInfo.setPatientPhone(patient.getPhone());
        // 订单状态  0 预约成功，待支付
        orderInfo.setOrderStatus(OrderStatusEnum.UNPAID.getStatus());
        // 插入数据库
        baseMapper.insert(orderInfo);
        log.info("【订单服务-保存订单】：{}", JSONObject.toJSONString(orderInfo));
        // 调医院服务获取签名信息
        SignInfoVo signInfoVo = hospitalFeignClient.getSignInfoVo(orderInfo.getHoscode());
        if (signInfoVo == null) {
            throw new RegisterException(ResultCodeEnum.PARAM_ERROR);
        }
        // 组装参数
        Map<String, Object> paramMap = new HashMap<>();
        // 排班信息
        paramMap.put("hoscode", orderInfo.getHoscode());
        paramMap.put("depcode", orderInfo.getDepcode());
        paramMap.put("hosScheduleId", orderInfo.getHosScheduleId());
        paramMap.put("reserveDate", new DateTime(orderInfo.getReserveDate()).toString("yyyy-MM-dd"));
        paramMap.put("reserveTime", orderInfo.getReserveTime());
        paramMap.put("amount", orderInfo.getAmount());
        // 就诊人信息
        paramMap.put("name", patient.getName());
        paramMap.put("certificatesType", patient.getCertificatesType());
        paramMap.put("certificatesNo", patient.getCertificatesNo());
        paramMap.put("sex", patient.getSex());
        paramMap.put("birthdate", patient.getBirthdate());
        paramMap.put("phone", patient.getPhone());
        paramMap.put("isMarry", patient.getIsMarry());
        paramMap.put("provinceCode", patient.getProvinceCode());
        paramMap.put("cityCode", patient.getCityCode());
        paramMap.put("districtCode", patient.getDistrictCode());
        paramMap.put("address", patient.getAddress());
        paramMap.put("isInsure", patient.getIsInsure());
        // 联系人
        paramMap.put("contactsName", patient.getContactsName());
        paramMap.put("contactsCertificatesType", patient.getContactsCertificatesType());
        paramMap.put("contactsCertificatesNo", patient.getContactsCertificatesNo());
        paramMap.put("contactsPhone", patient.getContactsPhone());
        // 时间戳
        paramMap.put("timestamp", HttpRequestHelper.getTimestamp());
        // 签名
        paramMap.put("sign", HttpRequestHelper.getSign(paramMap, signInfoVo.getSignKey()));

        // 调取医院系统的挂号接口  下订单
        JSONObject jsonObject = HttpRequestHelper.sendRequest(paramMap, signInfoVo.getApiUrl() + "/order/submitOrder");
        Integer code = jsonObject.getInteger("code");
        String message = jsonObject.getString("message");
        if (code == 200) {
            // 接受医院返回的数据，更新订单
            JSONObject data = jsonObject.getJSONObject("data");
            // 预约记录的唯一标识
            String hosRecordId = data.getString("hosRecordId");
            // 预约序号
            Integer number = data.getInteger("number");
            // 取号时间
            String fetchTime = data.getString("fetchTime");
            // 取号地点
            String fetchAddress = data.getString("fetchAddress");
            // 可预约数
            Integer reservedNumber = data.getInteger("reservedNumber");
            // 剩余预约数
            Integer availableNumber = data.getInteger("availableNumber");


            // 更新订单
            orderInfo.setHosRecordId(hosRecordId);
            orderInfo.setNumber(number);
            orderInfo.setFetchTime(fetchTime);
            orderInfo.setFetchAddress(fetchAddress);
            baseMapper.updateById(orderInfo);
            log.info("【订单服务-更新订单】：{}", JSONObject.toJSONString(orderInfo));
            // 发送mq消息： 更新号源 & 发送短信通知
            // 组装消息体
            OrderMqVo orderMqVo = new OrderMqVo();
            orderMqVo.setReservedNumber(reservedNumber);
            orderMqVo.setAvailableNumber(availableNumber);
            orderMqVo.setScheduleId(scheduleId);
            // 组装短信实体
            MsmVo msmVo = new MsmVo();
            msmVo.setPhone(patient.getPhone());
            // msmVo.setTemplateCode("");
            String reserveDate =
                    new DateTime(orderInfo.getReserveDate()).toString("yyyy-MM-dd")
                            + (orderInfo.getReserveTime() == 0 ? " 上午" : " 下午");
            Map<String, Object> map = new HashMap<String, Object>() {{
                put("title", orderInfo.getHosname() + "|" + orderInfo.getDepname() + "|" + orderInfo.getTitle());
                put("amount", orderInfo.getAmount());
                put("reserveDate", reserveDate);
                put("name", orderInfo.getPatientName());
                put("quitTime", new DateTime(orderInfo.getQuitTime()).toString("yyyy-MM-dd HH:mm"));
            }};
            msmVo.setParam(map);


            orderMqVo.setMsmVo(msmVo);

            // 发mq消息
            rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_ORDER, MqConst.ROUTING_ORDER, orderMqVo);
            log.info("【订单服务-发送更新号源】：{}", JSONObject.toJSONString(orderMqVo));

        } else {
            throw new RegisterException(message, code);
        }
        return orderInfo.getId();
    }

    /**
     * 分页查询订单列表
     * @param current 当页页码
     * @param limit   每页条数
     * @param queryVo 查询条件
     * @return
     */
    @Override
    public IPage<OrderInfo> findPageList(Integer current, Integer limit, OrderQueryVo queryVo) {
        // 分页对象
        Page<OrderInfo> page = new Page<>(current, limit);
        // 条件对象
        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(!StringUtils.isEmpty(queryVo.getUserId()), OrderInfo::getUserId, queryVo.getUserId())
                // 订单号
                .like(!StringUtils.isEmpty(queryVo.getOutTradeNo()), OrderInfo::getOutTradeNo, queryVo.getOutTradeNo())
                // 就诊人ID
                .eq(!StringUtils.isEmpty(queryVo.getPatientId()), OrderInfo::getPatientId, queryVo.getPatientId())
                // 就诊人姓名
                .like(!StringUtils.isEmpty(queryVo.getPatientName()), OrderInfo::getPatientName, queryVo.getPatientName())
                // 医院名称
                .like(!StringUtils.isEmpty(queryVo.getHosname()), OrderInfo::getHosname, queryVo.getHosname())
                // 订单状态
                .eq(!StringUtils.isEmpty(queryVo.getOrderStatus()), OrderInfo::getOrderStatus, queryVo.getOrderStatus())
                // 安排日期
                .eq(!StringUtils.isEmpty(queryVo.getReserveDate()), OrderInfo::getReserveDate, queryVo.getReserveDate())
                // 创建时间
                .ge(!StringUtils.isEmpty(queryVo.getCreateTimeBegin()), OrderInfo::getCreateTime, queryVo.getCreateTimeBegin())
                .le(!StringUtils.isEmpty(queryVo.getCreateTimeEnd()), OrderInfo::getCreateTime, queryVo.getCreateTimeEnd())
                .orderByAsc(OrderInfo::getReserveDate, OrderInfo::getReserveTime);
        // 分页查询
        Page<OrderInfo> orderInfoPage = baseMapper.selectPage(page, wrapper);
        orderInfoPage.getRecords().forEach(this::packOrderIndo);
        return orderInfoPage;
    }

    /**
     * 查询订单详情
     * @param orderId 订单编号
     * @return 订单信息
     */
    @Override
    public OrderInfo getOrderInfo(Long orderId) {
        OrderInfo orderInfo = baseMapper.selectById(orderId);
        if (orderInfo != null) {
            this.packOrderIndo(orderInfo);
        }
        return orderInfo;
    }

    /**
     * 查看订单详情列表
     * @param orderId 订单id
     * @return Map<String, Object>
     */
    @Override
    public Map<String, Object> show(Long orderId) {
        OrderInfo orderInfo = baseMapper.selectById(orderId);
        Map<String, Object> map = null;
        if (orderInfo != null) {
            this.packOrderIndo(orderInfo);
            map = new HashMap<>();
            map.put("orderInfo", orderInfo);
            map.put("patient", usersFeignClient.getPatientOrder(orderInfo.getPatientId()));
        }
        return map;
    }

    /**
     * 取消预约
     * @param orderId 订单id
     * @return 是否成功
     */
    @Override
    public Boolean cancleOrder(Long orderId) {
        //查询订单信息
        OrderInfo orderInfo = this.getOrderInfo(orderId);
        if (orderInfo == null) {
            throw  new RegisterException(ResultCodeEnum.PARAM_ERROR);
        }
        //判断当前时间是否可以退号
        if (new DateTime(orderInfo.getQuitTime()).isBeforeNow()) {
            throw new RegisterException(ResultCodeEnum.CANCEL_ORDER_NO);
        }
        //获取签名信息
        SignInfoVo signInfoVo = hospitalFeignClient.getSignInfoVo(orderInfo.getHoscode());
        if (signInfoVo == null) {
            throw new RegisterException(ResultCodeEnum.DATA_ERROR);
        }
        //组装参数
        Map<String , Object> paramMap = new HashMap<>();
        paramMap.put("hoscode", orderInfo.getHoscode());
        paramMap.put("hosRecordId", orderInfo.getHosRecordId());
        paramMap.put("timestamp", HttpRequestHelper.getTimestamp());
        paramMap.put("sign",HttpRequestHelper.getSign(paramMap,signInfoVo.getSignKey()));
        //退号  对接医院系统取消订单
        JSONObject jsonObject = HttpRequestHelper.sendRequest(paramMap, signInfoVo.getApiUrl() + "/order/updateCancelStatus");
        log.info("【订单服务-调用医院系统取消订单】：{}",JSONObject.toJSONString(jsonObject));
        if (200!=jsonObject.getInteger("code")) {
            //医院系统未取消成功，抛异常
            throw new RegisterException(jsonObject.getString("message"), jsonObject.getInteger("code"));
        }else {
            //判断订单是否已支付
            if (Objects.equals(OrderStatusEnum.PAID.getStatus(), orderInfo.getOrderStatus())) {
                //已支付，调微信退款接口退款
                if (!weixinService.refund(orderId)) {
                    throw new RegisterException("微信退款失败", ResultCodeEnum.FAIL.getCode());
                }
            }

            //更新订单状态
            orderInfo.setOrderStatus(OrderStatusEnum.CANCLE.getStatus()); //-1 :取消预约
            baseMapper.updateById(orderInfo);
            // 发mq消息 ：更新号源 & 发送短信通知
            //组装消息
            OrderMqVo orderMqVo = new OrderMqVo();
            orderMqVo.setScheduleId(orderInfo.getHosScheduleId());//医院系统的排班主键
            //不设置可预约数与剩余预约数，将来接收方将剩余预约数 +1 即可
            // orderMqVo.setAvailableNumber();
            //短信实体
            MsmVo msmVo = new MsmVo();
            msmVo.setPhone(orderInfo.getPatientPhone());
            // msmVo.setTemplateCode("");  // 当前使用的是测试模板
            String reserveDate = new DateTime(orderInfo.getReserveDate()).toString("yyyy-MM-dd") + (orderInfo.getReserveTime()==0 ? "上午": "下午");
            Map<String,Object> param = new HashMap<String,Object>(){{
                put("title", orderInfo.getHosname()+"|"+orderInfo.getDepname()+"|"+orderInfo.getTitle());
                put("reserveDate", reserveDate);
                put("name", orderInfo.getPatientName());
            }};
            msmVo.setParam(param);

            orderMqVo.setMsmVo(msmVo);

            //发送
            rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_ORDER,MqConst.ROUTING_ORDER,orderMqVo);
            log.info("【订单服务-发送更新号源消息】：{}",JSONObject.toJSONString(orderMqVo));
            return true;

        }
    }

    /**
     * 就诊提醒
     */
    @Override
    public void patientTips() {
        //获取就诊日期为当天的订单
        List<OrderInfo> orderInfoList = baseMapper.selectList(new LambdaQueryWrapper<OrderInfo>()
                .eq(OrderInfo::getReserveDate, new DateTime().toString("yyyy-MM-dd")));
        if (!orderInfoList.isEmpty()) {
            orderInfoList.forEach((orderInfo)->{
                String phone = orderInfo.getPatientPhone();
                //短信内容
                String reserveDate = new DateTime(orderInfo.getReserveDate()).toString("yyyy-MM-dd") + (orderInfo.getReserveTime()==0 ? "上午": "下午");
                Map<String,Object> param = new HashMap<String,Object>(){{
                    put("title", orderInfo.getHosname()+"|"+orderInfo.getDepname()+"|"+orderInfo.getTitle());
                    put("reserveDate", reserveDate);
                    put("name", orderInfo.getPatientName());
                }};
                //短信实体
                MsmVo msmVo = new MsmVo();
                msmVo.setPhone(orderInfo.getPatientPhone());
                msmVo.setParam(param);
                //发mq消息，由短信服务消费发送短信通知
                rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_MSM,MqConst.ROUTING_MSM_ITEM,msmVo);
            });
        }
    }

    /**
     * 预约统计
     * @param queryVo 查询条件
     * @return Map
     */
    @Override
    public Map<String, Object> getCountMap(OrderCountQueryVo queryVo) {
        List<OrderCountVo> orderCountVoList = baseMapper.getCountList(queryVo);
        Map<String, Object> map = new HashMap<>();
        if (orderCountVoList != null) {
            //日期列表
            List<String> dateList = orderCountVoList.stream().map(OrderCountVo::getReserveDate).collect(Collectors.toList());
            //数量列表
            List<Integer> countList = orderCountVoList.stream().map(OrderCountVo::getCount).collect(Collectors.toList());
            map.put("dateList",dateList);
            map.put("countList",countList);
        }
        return map;
    }


    private void packOrderIndo(OrderInfo orderInfo) {
        String orderStatus = OrderStatusEnum.getStatusNameByStatus(orderInfo.getOrderStatus());
        orderInfo.getParam().put("orderStatusString", orderStatus);
    }

}